
// F. Chaxel 2010

#include "SerialMica.h"
#include <fcntl.h>
#include <termio.h>
#include <string.h>

#include <iostream>
using namespace std;

// needed to call the event (advise)
struct AdviseEventData
{
	IMicaAdvise *Caller;		// caller address
	struct MicaPacket Pkt;	// parameters
};
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
SerialMica::SerialMica()
{
	fd_serial=-1;
	Advised_Object=NULL;
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
SerialMica::~SerialMica()
{
	Disconnect();
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void SerialMica::Advise(IMicaAdvise *Listener)
{

	if (Advised_Object==NULL)	// only one listener
		Advised_Object=Listener;
}
void SerialMica::UnAdvise(IMicaAdvise *Listener)
{
	 
	if (Advised_Object==Listener)
		Advised_Object=NULL;	// not really thread safe
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void *SerialMica::MicaAdviseThread( void *ptrAdvise )
{

	//.. used to fire an event in a separated thread
	try
	{		
		// get the caller address and the parameters
		AdviseEventData *AdvisedData=(AdviseEventData*)ptrAdvise;

		IMicaAdvise *Caller= AdvisedData->Caller;

		Caller->MicaDataReceive(&AdvisedData->Pkt);

		// delete dynamicaly allocated params
		delete AdvisedData;
	}
	catch (...){}

	return NULL;
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
bool SerialMica::Connect(char *serport)
{
	struct termios newtio;

	fd_serial = open(serport,O_RDWR| O_NOCTTY | O_NONBLOCK);

	if (fd_serial<0)
		return false;

	// set new port settings for non canonical input processing 
	// no control modem, enable reception
	newtio.c_cflag = B19200 | CS8 | CLOCAL | CREAD ;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;
	newtio.c_lflag = 0;       //ICANON;
	
	// wait 0,2s for each byte one by one
	newtio.c_cc[VMIN]=1;
	newtio.c_cc[VTIME]=2;	// Timeout 0,2s between each byte in one frame

 	tcflush(fd_serial, TCIFLUSH);    
	tcsetattr(fd_serial, TCSANOW, &newtio);

	return true;
}
//////////////////////////////////////////////////////////////////////
void SerialMica::Disconnect()
{
	
	if (fd_serial>=0)
	{
		CancelThreadRun();
		close(fd_serial);
		fd_serial=-1;
	}
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void SerialMica::Run()
{
	unsigned char* buf;
	int res;
	int byteNum;

	struct MicaPacket ReceiveFrame;

	if (fd_serial<0)
		return;

	byteNum=0;

	for (;;)
	{
		buf=(unsigned char*)&ReceiveFrame;
		buf+=byteNum;

		res = read(fd_serial,buf,1); // receive

		if (res>0)
		{
			// first byte ?
			if (byteNum==0)
			{
				if (ReceiveFrame.Start7e==0x7E)
				{					
					byteNum++;
					// Ready for the biggest packet
					// maybe changed during frame reception
					ReceiveFrame.Lenght=MICA_DATA_MAXSIZE;
				}
			}
			else
			{
				byteNum++;
			}
			
			// full frame ?
			if ((byteNum==ReceiveFrame.Lenght+7)||(byteNum==sizeof(ReceiveFrame)))
			{
				// when not max size frame, copy the crc at it's reserved place
				//if max size frame, this operation has no effect
				ReceiveFrame.Crc=(short)*(buf-1);

				if ((Advised_Object!=NULL)&&(ReceiveFrame.Crc==0x0001))
				{

					struct AdviseEventData* a;
					pthread_t idThread;

					a=new struct AdviseEventData;
					memcpy(&a->Pkt,&ReceiveFrame,sizeof(ReceiveFrame));
					a->Caller=Advised_Object;
		
					// Send to user via a separated thread
					pthread_create(&idThread, NULL, &SerialMica::MicaAdviseThread, a);
					sleep(0);					
				}

				byteNum=0;
			}			
		}
		else
		{
			byteNum=0;	// TimeOut		
		}
	}
}
//////////////////////////////////////////////////////////////////////
void SerialMica::Write(char *buf, int lenght)
{
	if (fd_serial<0)
		return;

	write(fd_serial,buf,lenght);
}
//////////////////////////////////////////////////////////////////////
bool SerialMica::IsOpen()
{
	return (fd_serial>=0);
}


